﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using DocumentsTagger.DM;
using System.Collections.ObjectModel;
using System.Windows.Input;
using DocumentsTagger.UI.Commands;
using DocumentsTagger.BL;
using DocumentsTagger.DAL;
using DocumentsTagger.UI.Model;
using System.ComponentModel;

namespace DocumentsTagger.UI.ViewModel
{
    /// <summary>
    /// This is the ViewModel of the document (the details part in the master-details View Models).
    /// It is hosted within MainWindowViewModel.
    /// Holds details about the currently selected document.
    /// </summary>
    public class DocumentViewModel : DependencyObject, INotifyPropertyChanged, IDisposable
    {
        #region members
        private readonly TaggerBL r_TaggerBL = new TaggerBL(TaggerXMLDal.GetInstance());
        private readonly Document r_Document;
        #endregion

        #region properties
        /// <summary>
        /// gets or sets the document's name
        /// </summary>
        public string FileName { get; set; }
        
        /// <summary>
        /// gets or sets the document's folder.
        /// </summary>
        public string FolderPath { get; set; }

        /// <summary>
        /// gets or sets the document's full path
        /// </summary>
        public string FullPath { get; set; }

        /// <summary>
        /// gets or sets the Document's creation date
        /// </summary>
        public DateTime CreationDate { get; set; }
        
        /// <summary>
        /// gets or sets the Document's Size
        /// </summary>
        public String Size{ get; set; }

        /// <summary>
        /// a string holding the currently entered tag for adding to the document
        /// </summary>
        public string AddedTag { get; set; }

        /// <summary>
        /// a string holding the currently entered tag for adding to the declined tags of document
        /// </summary>
        public string AddedDeclinedTag { get; set; }

        /// <summary>
        /// a list of UI Parts of tags - for showing the accepted tags of the document
        /// </summary>
        public ObservableCollection<TagUIModel> AcceptedTags { get; set; }
        
        /// <summary>
        /// a list of UI Parts of tags - for showing the suggested tags of the document
        /// </summary>
        public ObservableCollection<TagUIModel> SuggestedTags { get; set; }
        
        /// <summary>
        /// a list of UI Parts of tags - for showing the declined tags of the document
        /// </summary>
        public ObservableCollection<TagUIModel> DeclinedTags { get; set; }

        /// <summary>
        /// command for binding the adding of declined tag to document
        /// </summary>
        public ICommand AddDeclinedTagToDocumentCommandProp
        {
            get { return (ICommand)GetValue(AddDeclinedTagToDocumentCommandPropProperty); }
            set { SetValue(AddDeclinedTagToDocumentCommandPropProperty, value); }
        }

        /// <summary>
        /// command for binding the adding of declined tag to document
        /// </summary>
        public ICommand AddTagToDocumentCommandProp
        {
            get { return (ICommand)GetValue(AddTagToDocumentCommandPropProperty); }
            set { SetValue(AddTagToDocumentCommandPropProperty, value); }
        }

        /// <summary>
        /// command for binding the adding of declined tag to document
        /// </summary>
        public ICommand AcceptSuggestedTagCommandProp
        {
            get { return (ICommand)GetValue(AcceptSuggestedTagCommandPropProperty); }
            set { SetValue(AcceptSuggestedTagCommandPropProperty, value); }
        }

        /// <summary>
        /// command for binding the adding of declined tag to document
        /// </summary>
        public ICommand RemoveAcceptedTagCommandProp
        {
            get { return (ICommand)GetValue(RemoveAcceptedTagCommandPropProperty); }
            set { SetValue(RemoveAcceptedTagCommandPropProperty, value); }
        }

        /// <summary>
        /// command for binding the adding of declined tag to document
        /// </summary>
        public ICommand DeclineSugestedTagCommandProp
        {
            get { return (ICommand)GetValue(DeclineSugestedTagCommandPropProperty); }
            set { SetValue(DeclineSugestedTagCommandPropProperty, value); }
        }

        /// <summary>
        /// command for binding the adding of declined tag to document
        /// </summary>
        public ICommand RemoveDeclinedTagCommandProp
        {
            get { return (ICommand)GetValue(RemoveDeclinedTagCommandPropProperty); }
            set { SetValue(RemoveDeclinedTagCommandPropProperty, value); }
        }

        #region Dependency Properties
        // Using a DependencyProperty as the backing store for RemoveDeclinedTagCommand.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty RemoveDeclinedTagCommandPropProperty =
            DependencyProperty.Register("RemoveDeclinedTagCommandProp", typeof(ICommand), typeof(DocumentViewModel), new UIPropertyMetadata(null));

        // Using a DependencyProperty as the backing store for DeclineSugestedTagCommandProp.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty DeclineSugestedTagCommandPropProperty =
            DependencyProperty.Register("DeclineSugestedTagCommandProp", typeof(ICommand), typeof(DocumentViewModel), new UIPropertyMetadata(null));

        // Using a DependencyProperty as the backing store for RemoveAcceptedTagCommand.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty RemoveAcceptedTagCommandPropProperty =
            DependencyProperty.Register("RemoveAcceptedTagCommandProp", typeof(ICommand), typeof(DocumentViewModel), new UIPropertyMetadata(null));

        // Using a DependencyProperty as the backing store for AddDeclinedTagToDocumentCommand.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty AddDeclinedTagToDocumentCommandPropProperty =
            DependencyProperty.Register("AddDeclinedTagToDocumentCommandProp", typeof(ICommand), typeof(DocumentViewModel), new UIPropertyMetadata(null));

        // Using a DependencyProperty as the backing store for AddTagToDocumentCommand.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty AddTagToDocumentCommandPropProperty =
            DependencyProperty.Register("AddTagToDocumentCommandProp", typeof(ICommand), typeof(DocumentViewModel), new UIPropertyMetadata(null));

        // Using a DependencyProperty as the backing store for AcceptSuggestedTagCommandProp.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty AcceptSuggestedTagCommandPropProperty =
            DependencyProperty.Register("AcceptSuggestedTagCommandProp", typeof(ICommand), typeof(DocumentViewModel), new UIPropertyMetadata(null));
        #endregion 
        #endregion

        #region events
        public event PropertyChangedEventHandler PropertyChanged;
        #endregion

        #region ctor
        /// <summary>
        /// creates a new view model 
        /// </summary>
        /// <param name="doc">the underlying document</param>
        public DocumentViewModel(Document doc)
        {
            r_Document = doc;

            this.FileName = doc.FileName;
            this.FolderPath = doc.FilePath;
            this.FullPath = doc.FullPath;
            this.CreationDate = doc.CreationDate;
            this.Size = this.parseFileSize( doc.Size);

            AcceptedTags = new ObservableCollection<TagUIModel>();
            SuggestedTags = new ObservableCollection<TagUIModel>();
            DeclinedTags = new ObservableCollection<TagUIModel>();

            foreach (Tag tag in doc.AcceptedTags)
            {
                AcceptedTags.Add(new TagUIModel(tag));
            }

            foreach (Tag tag in doc.SuggestedTags)
            {
                SuggestedTags.Add(new TagUIModel(tag));
            }

            foreach (Tag tag in doc.DeclinedTags)
            {
                DeclinedTags.Add(new TagUIModel(tag));
            }


            this.AddTagToDocumentCommandProp = new AddTagToDocumentCommand(this);
            NotifyPropertyChanged("AddTagToDocumentCommandProp");
            this.AddDeclinedTagToDocumentCommandProp = new AddDeclinedTagToDocumentCommand(this);
            NotifyPropertyChanged("AddDeclinedTagToDocumentCommandProp");
            this.RemoveAcceptedTagCommandProp = new RemoveAcceptedTagCommand(this);
            NotifyPropertyChanged("RemoveAcceptedTagCommandProp");
            this.DeclineSugestedTagCommandProp = new DeclineSugestedTagCommand(this);
            NotifyPropertyChanged("DeclineSugestedTagCommandProp");
            this.AcceptSuggestedTagCommandProp = new AcceptSuggestedTagCommand(this);
            NotifyPropertyChanged("AcceptSuggestedTagCommandProp");
            this.RemoveDeclinedTagCommandProp = new RemoveDeclinedTagCommand(this);
            NotifyPropertyChanged("RemoveDeclinedTagCommandProp");

        }

        private string parseFileSize(long fileSize)
        {
            StringBuilder sb = new StringBuilder();
            float megabytes = (float)(fileSize / 1024.0 / 1024.0);
            sb.Append(String.Format("{0:0.##}", megabytes));
            sb.Append(" MB, (");
            sb.Append(fileSize);
            sb.Append(" Bytes)");

            return sb.ToString();
        }
        #endregion

        #region methods
        /// <summary>
        /// handles UI request to add a typed tag to accepted tags of this document
        /// </summary>
        /// <param name="tagValue">the tag name</param>
        internal void AddTagToDocument(string tagValue)
        {
            this.r_TaggerBL.AddTag(r_Document, tagValue);
            this.AcceptedTags.Add(new TagUIModel(TagsFactory.GetTag(tagValue)));
            this.r_Document.AddAcceptedTag(TagsFactory.GetTag(tagValue));
        }

        /// <summary>
        /// handles UI request to remove a tag from the accepted tags of this document
        /// </summary>
        internal void RemoveTagFromAcceptedTagsList()
        {
            int i = 0;
            while (i < AcceptedTags.Count)
            {
                TagUIModel item = AcceptedTags[i];
                if (item.IsChecked)
                {
                    AcceptedTags.Remove(item);
                    r_TaggerBL.RemoveAcceptedTagFromDocument(this.r_Document, item.Tag);
                }
                else
                {
                    i++;
                }
            }
        }
        
        /// <summary>
        /// handles UI request to Decline a suggested tag from the suggested tags of this document
        /// and to place the tag in the declined tags list
        /// </summary>
        internal void DeclineSuggestedTags()
        {
            int i = 0;
            while (i < this.SuggestedTags.Count)
            {
                if (this.SuggestedTags[i].IsChecked)
                {
                    this.SuggestedTags[i].IsChecked = false;
                    r_TaggerBL.DeclineSuggestedTagsForDocument(r_Document, this.SuggestedTags[i].Tag);
                    this.DeclinedTags.Add(this.SuggestedTags[i]);
                    this.SuggestedTags.RemoveAt(i);
                }
                else
                {
                    i++;
                }
            }
        }

        /// <summary>
        /// handles UI request to Accept a suggested tag from the suggested tags of this document
        /// and to place that tag in the accepted tags list
        /// </summary>
        internal void AcceptSuggestedTags()
        {
            int i = 0;
            while (i < this.SuggestedTags.Count)
            {
                if (this.SuggestedTags[i].IsChecked)
                {
                    this.SuggestedTags[i].IsChecked = false;
                    r_TaggerBL.AcceptSuggestedTag(r_Document, this.SuggestedTags[i].Tag);
                    this.AcceptedTags.Add(this.SuggestedTags[i]);
                    this.SuggestedTags.RemoveAt(i);

                }
                else
                {
                    i++;
                }
            }
        }

        /// <summary>
        /// handles UI request to remove  a declined tag from the declined tags of this document
        /// </summary>
        internal void RemoveDeclinedTags()
        {
            int i = 0;
            while (i < this.DeclinedTags.Count)
            {
                if (this.DeclinedTags[i].IsChecked)
                {
                    r_TaggerBL.RemoveDeclineTagFromDocument(r_Document, this.DeclinedTags[i].Tag);
                    this.DeclinedTags.RemoveAt(i);
                }
                else
                {
                    i++;
                }
            }
        }

        /// <summary>
        /// handles UI request to add a typed tag to declined tags of this document
        /// </summary>
        /// <param name="tagValue">the tag name</param>
        internal void AddDeclinedTagToDocument(string tagValue)
        {
            this.r_TaggerBL.AddDeclinedTagToDocument(r_Document, TagsFactory.GetTag(tagValue));
            this.DeclinedTags.Add(new TagUIModel(TagsFactory.GetTag(tagValue)));
            this.r_Document.AddDeclinedTag(TagsFactory.GetTag(tagValue));
        }

        /// <summary>
        /// initiated the notification process
        /// </summary>
        /// <param name="info"></param>
        private void NotifyPropertyChanged(String info)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(info));
            }
        }

        public void Dispose()
        {
            AcceptedTags.Clear();
            SuggestedTags.Clear();
            DeclinedTags.Clear();

            AcceptedTags = null;
            SuggestedTags = null;
            DeclinedTags = null;
        }
        #endregion

        
        
    }
}
